package com.lfy.generator.service;

import cn.hutool.db.Entity;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.db.handler.EntityListHandler;
import cn.hutool.db.sql.SqlExecutor;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.lfy.generator.bean.Table;
import com.lfy.generator.constant.FileConst;
import com.lfy.generator.utils.MyVelocityTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Author: 李丰翼
 * @DateTime: 2020/11/7 0007 14:25
 * @Description:
 */
@Slf4j
@Service
public class GeneratorService {


    public List<Table> list(String url, String user, String pass) throws SQLException {
        DataSource ds = new SimpleDataSource(converUrl(url), user, pass, "com.mysql.cj.jdbc.Driver");
        Connection connection = ds.getConnection();
        String sql = "select table_name name, engine engine, table_comment comment, create_time createTime from information_schema.tables where table_schema = (select database())";
        List<Entity> entityList = SqlExecutor.query(connection, sql, new EntityListHandler());
        return entityList.stream().map(e -> e.toBean(Table.class)).collect(Collectors.toList());

    }

    public byte[] generatorCode(HashMap<String, String> settingMap, String[] include) {
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOpen(false);
        gc.setFileOverride(true);
        gc.setAuthor(settingMap.getOrDefault("globalConfig.author", "lfy"));
        gc.setSwagger2(settingMap.containsKey("globalConfig.swagger2"));
        gc.setActiveRecord(settingMap.containsKey("globalConfig.activeRecord"));
        gc.setBaseResultMap(settingMap.containsKey("globalConfig.baseResultMap"));
        if (settingMap.containsKey("globalConfig.entityName")) {
            gc.setEntityName(settingMap.getOrDefault("globalConfig.entityName", "%s"));
        }
        if (settingMap.containsKey("globalConfig.mapperName")) {
            gc.setMapperName(settingMap.getOrDefault("globalConfig.mapperName", "%sDao"));
        }
        if (settingMap.containsKey("globalConfig.xmlName")) {
            gc.setXmlName(settingMap.getOrDefault("globalConfig.xmlName", "%sMapper"));
        }
        if (settingMap.containsKey("globalConfig.serviceName")) {
            gc.setServiceName(settingMap.getOrDefault("globalConfig.serviceName", "I%sService"));
        }
        if (settingMap.containsKey("globalConfig.serviceImplName")) {
            gc.setServiceImplName(settingMap.getOrDefault("globalConfig.serviceImplName", "%sServiceImpl"));
        }
        if (settingMap.containsKey("globalConfig.controllerName")) {
            gc.setControllerName(settingMap.getOrDefault("globalConfig.controllerName", "%sController"));
        }
        mpg.setGlobalConfig(gc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(settingMap.getOrDefault("packageInfo.parent", "com.lfy.generator"));
        if (settingMap.containsKey("packageInfo.entity")) {
            pc.setEntity(settingMap.get("packageInfo.entity"));
        }
        if (settingMap.containsKey("packageInfo.mapper")) {
            pc.setMapper(settingMap.get("packageInfo.mapper"));
        }
        if (settingMap.containsKey("packageInfo.xml")) {
            pc.setXml(settingMap.get("packageInfo.xml"));
        }
        if (settingMap.containsKey("packageInfo.service")) {
            pc.setService(settingMap.get("packageInfo.service"));
        }
        if (settingMap.containsKey("packageInfo.serviceImpl")) {
            pc.setServiceImpl(settingMap.get("packageInfo.serviceImpl"));
        }
        if (settingMap.containsKey("packageInfo.controller")) {
            pc.setController(settingMap.get("packageInfo.controller"));
        }
        mpg.setPackageInfo(pc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(converUrl(settingMap.get("dataSource.url")));
        dsc.setUsername(settingMap.get("dataSource.user"));
        dsc.setPassword(settingMap.get("dataSource.pass"));
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        mpg.setDataSource(dsc);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 配置自定义输出模板
        //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        templateConfig.setEntity("templates/entity.java");
        templateConfig.setMapper("templates/mapper.java");
        templateConfig.setXml("templates/mapper.xml");
        templateConfig.setService("templates/service.java");
        templateConfig.setServiceImpl("templates/serviceImpl.java");
        templateConfig.setController("templates/controller.java");
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setRestControllerStyle(true);
        strategy.setEntityLombokModel(settingMap.containsKey("strategy.entityLombokModel"));
        strategy.setEntitySerialVersionUID(settingMap.containsKey("strategy.entitySerialVersionUID"));
        strategy.setInclude(include);
        mpg.setStrategy(strategy);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
                Map<String, Object> map = new HashMap<>();
                map.put("parent", settingMap.getOrDefault("packageInfo.parent", "com.lfy.generator"));
                this.setMap(map);
            }
        };
        mpg.setCfg(cfg);
        //使用自定义模版引擎
        MyVelocityTemplateEngine myVelocityTemplateEngine = new MyVelocityTemplateEngine();
        mpg.setTemplateEngine(myVelocityTemplateEngine);
        //生成基础文件
        mpg.execute();

        ZipOutputStream zip = myVelocityTemplateEngine.getZip();
        //生成common包下的工具类
        String os = System.getProperty("os.name").toLowerCase();
        String common = "";
        String pom = "";
        String config = "";
        String parent = mpg.getPackageInfo().getParent();
        parent = parent.replace(".", "\\");
        parent = "\\" + parent + "\\";
        try {

            if (os.contains("windows")) {
                common = FileConst.WIN_COMMON_PATH;
                config = FileConst.WIN_CONFIG_PATH;
                pom = FileConst.WIN_POM_PATH;


            } else {
                common = FileConst.LINUX_COMMON_PATH;
                config = FileConst.LINUX_CONFIG_PATH;
                pom = FileConst.LINUX_POM_PATH;

            }
            //common包下文件添加到zip
            File commonFiles = new File(common);
            String[] commonFileList = commonFiles.list();
            for (int i = 0; i < Objects.requireNonNull(commonFileList).length; i++) {
                writeIntoZip(common + "/" + commonFileList[i], zip, gc.getOutputDir() + parent + "common\\" + commonFileList[i]);
            }
            //config包下文件添加到zip
            File configFiles = new File(config);
            String[] configFileList = configFiles.list();
            for (int i = 0; i < Objects.requireNonNull(configFileList).length; i++) {
                writeIntoZip(config + "/" + configFileList[i], zip, gc.getOutputDir() + parent + "config\\" + configFileList[i]);
            }
            //pom文件添加到zip
            writeIntoZip(pom, zip, gc.getOutputDir() + "pom.xml");
        } catch (Exception e) {
            log.error("文件找不到！");
            e.printStackTrace();
        }
        //生成结束
        IOUtils.closeQuietly(zip);
        //输出字节数组
        try {
            ByteArrayOutputStream outputStream = myVelocityTemplateEngine.getOutputStream();
            return outputStream.toByteArray();
        } catch (Exception e) {
            return new byte[]{};
        }

    }

    /**
     * ip转为数据库连接url
     *
     * @param ip
     * @return
     */
    public String converUrl(String ip) {
        return "jdbc:mysql://" + ip + "?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8";
    }

    /**
     * 将文件写入到zip
     *
     * @param sourcePath
     * @param zip
     * @param entryName
     * @throws IOException
     */
    public void writeIntoZip(String sourcePath, ZipOutputStream zip, String entryName) throws IOException {

        InputStream inputStream = new FileInputStream(sourcePath);
        zip.putNextEntry(new ZipEntry(entryName));
        IOUtils.write(IOUtils.toString(inputStream, StandardCharsets.UTF_8), zip, "UTF-8");
        IOUtils.closeQuietly(inputStream);
        zip.closeEntry();
        log.info("文件：" + entryName);
    }
}
